

package raft

import (
	"errors"

	pb "go.etcd.io/etcd/raft/v3/raftpb"
)

// 引导程序通过附加配置
// 对所提供的对等机进行更改来初始化RawNode以供首次使用。如果存储
// 为非空，则此方法返回错误。
// 
// 建议应用程序不调用此方法，而是通过设置第一个索引大于1的存储和将所需ConfState存储为其初始状态的
// 手动引导其状态。
func (rn *RawNode) Bootstrap(peers []Peer) error {
	if len(peers) == 0 {
		return errors.New("must provide at least one peer to Bootstrap")
	}
	lastIndex, err := rn.raft.raftLog.storage.LastIndex()
	if err != nil {
		return err
	}

	if lastIndex != 0 {
		return errors.New("can't bootstrap a nonempty Storage")
	}

	// 我们伪造了上面的初始条目，但没有任何内容被
	// 保留。从一个空的硬状态开始（因此第一个就绪状态将
	// 发出一个硬状态更新以使应用程序持久化）。
	rn.prevHardSt = emptyState

	rn.raft.becomeFollower(1, None)	// 切换成Follower状态，因为节点初次启动，所以任期号为l
	ents := make([]pb.Entry, len(peers))
	for i, peer := range peers {
		// 根据传递的节点列表，创建对应的ConfChange实例，其Type是ConfChangeAddNode,表示添加指定节点
		cc := pb.ConfChange{Type: pb.ConfChangeAddNode, NodeID: peer.ID, Context: peer.Context}
		data, err := cc.Marshal()	// 序列化ConfChange记录
		if err != nil {
			return err
		}
		// 将ConfChange记录序列化后的数据封装成EntryConfChange类型的Entry记录
		ents[i] = pb.Entry{Type: pb.EntryConfChange, Term: 1, Index: uint64(i + 1), Data: data}
	}
	rn.raft.raftLog.append(ents...)	// 将EntryConfChange记录追加到raftLog中

	// 直接修改raftLog.committed佳，提交上述EntryConfChange记录
	rn.raft.raftLog.committed = uint64(len(ents))
	// 为节点列表中的每个节点都创建对应的Progress实例
	for _, peer := range peers {
		rn.raft.applyConfChange(pb.ConfChange{NodeID: peer.ID, Type: pb.ConfChangeAddNode}.AsV2())
	}
	return nil
}
